import React from 'react'
import HelloWorld from './components/helloworld'
import Main from './components/Main'
import SonToFather from './components/SonToFather'

// 编写一个组件：
class App extends React.Component {
  // 生命周期：
  /**
   * 1、constructor：
   * 如果不初始化 state 或者不进行方法绑定，则不需要 React 组件实现构造函数
   * constructor 通常只做两件事：
   *  1、通过给 this.state 赋值对象来初始化内部的 state
   *  2、为事件绑定 this
   */

  // 2、执行 render 函数

  // 3、组件被渲染到 DOM，被挂载到 DOM 上面 （componentDidMount）
  /**
   * componentDidMount() 会在组件挂载后（插入 DOM 树中）立即调用
   * componentDidMount 中通常在哪里进行操作呢：
   *  1、依赖于 DOM 的操作可以在这里进行
   *  2、在此发送网络请求最好的地方（官方建议）
   *  3、可以在此处添加一些订阅（会在 componentWillUnmount 中取消订阅）
   */

  // 4、组件的 DOM 被更新完成，DDM 发生更新 （componentDidUpdate）
  /**
   * componentDidUpdate 会在更新后会被立即调用，首次渲染不会执行此方法
   * 当组件更新之后，可以在此对 DOM 进行操作
   * 如果你对更新前后的 props 进行了比较，也可以选择在此进行网络请求，（例如：当 props 未发生变化时，则不会执行网络请求）
   */

  // 5、组件从 DOM 中被卸载，从 DOM 中被移除 （componentWillUnmount）
  /**
   * componentWillUnmount 会在组件卸载以及销毁之前直接调用
   * 在此方法中执行必要的清理操作
   * 例如：清除 timer ，取消网络请求或清楚在 componentDidMount 中创建的订阅等
   */
  // 1、执行 constructor：
  constructor() {
    super()

    console.log('App construstor')
    this.state = {
      msg: '哈哈哈',

      // 生命周期的子组件展示
      isShow: true,

      // 父子通信数据（父传子）：
      mainArr: ['main测试数据1', 'main测试数据2', 'main测试数据3'],
      // 父子通信数据（子传父）：
      count: 100
    }
  }

  // 2、执行 render 函数
  render() {
    const { msg, isShow, mainArr, count } = this.state

    console.log('App render')
    return (
      <div>
        {/* 生命周期： */}
        <h4>生命周期：</h4>
        <h4>{msg}</h4>
        <button onClick={(e) => this.changeIsShow()}>点击隐藏文本</button>
        {isShow && <HelloWorld />}

        {/* 组件嵌套以及组件通信： */}
        {/* 
          父传子：
          1、父组件通过 属性=值 的形式来传递数据给子组件
          2、子组件通过 props 参数获取父组件传递过来的数据：
            constructor(props) {
              super(props)
              this.state = {}
            }
          对于传递到子组件的数据，有时候我们可能希望进行验证
            当然，如果项目中默认继承了 Flow 或者 TypeScript，那么就可以直接进行类型验证
            但是，即使我们没有使用 Flow 或者 TypeScript，也可以通过 prop-types 库来进行参数验证
          写法：
          1、在子组件中先引入 prop-types：
          import PropTypes from 'prop-types'
          2、在子组件中验证（是什么类型就写什么类型，如果是必传值，加上 .isRequired）：
          子组件名.propTypes = {
            传入数据名1: PropTypes.array.isRequired,
            传入数据名2: PropTypes.string
          }
          
          props 传参的默认值：
          写法：
          子组件名.defaultProps = {
            传入数据: 默认值
          }


          子传父：
          在 React 中同样是通过 props 来传递消息，只是让父组件给子组件传递一个回调函数，在子组件中调用这个函数即可
        */}
        <h4>组件嵌套以及组件通信（父传子）：</h4>
        <Main mainArr={mainArr} />
        {/* 当父组件没有传值给子组件的时候，就是在子组件中使用默认值 */}
        <Main />

        <h4>组件嵌套以及组件通信（子传父）：</h4>
        <SonToFather
          count={count}
          changeCount={(num) => {
            this.newCount(num)
          }}
        />
      </div>
    )
  }

  // 点击隐藏子组件文本
  changeIsShow() {
    this.setState({
      isShow: !this.state.isShow
    })
  }

  // 根据子组件点击传过来的 num 修改 count
  newCount(num) {
    this.setState({
      count: this.state.count + num
    })
  }
}

export default App
